#include <e.h>
#include "e_mod_main.h"

#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include "i8k.h"

typedef struct _Instance Instance;
typedef struct _Ei8k Ei8k;


struct _Instance
{
    E_Gadcon_Client *gcc;
    Evas_Object *ei8k_obj;
    Ei8k *ei8k;
    Ecore_Timer *monitor;
};

struct _Ei8k
{
    Instance *inst;
    Evas_Object *ei8k_obj;
};

static int i8k_fd;

static Ecore_Timer *_ei8k_monitor = NULL;
static int lf_stat = 0;
static int rf_stat = 0;
static int temp = 0;


static E_Gadcon_Client *_gc_init (E_Gadcon * gc, const char *name,
                                  const char *id, const char *style);
static void _gc_shutdown (E_Gadcon_Client * gcc);
static void _gc_orient (E_Gadcon_Client * gcc);
static char *_gc_label (void);
static Evas_Object *_gc_icon (Evas * evas);

static void _ei8k_cb_mouse_down (void *data, Evas * e, Evas_Object * obj,
                                 void *event_info);
static void _ei8k_menu_cb_configure (void *data, E_Menu * m, E_Menu_Item * mi);
static void _ei8k_menu_cb_post (void *data, E_Menu * m);

static Ei8k *_ei8k_new (Evas * evas);
static void _ei8k_free (Ei8k * ei8k);
static int _ei8k_cb_check (void *data);

static int _ei8k_fan_check(void *data);
static Config_Item *_ei8k_config_item_get (const char *id);

static E_Config_DD *conf_edd = NULL;
static E_Config_DD *conf_item_edd = NULL;

Config *ei8k_config = NULL;
//Config_Item *ei8k_config_item = NULL;


static const E_Gadcon_Client_Class _gc_class =
    {
        GADCON_CLIENT_CLASS_VERSION,
        "ei8k", {_gc_init, _gc_shutdown, _gc_orient, _gc_label, _gc_icon},
        E_GADCON_CLIENT_STYLE_PLAIN
    };



static E_Gadcon_Client *
_gc_init (E_Gadcon * gc, const char *name, const char *id, const char *style)
{
    Evas_Object *o;
    E_Gadcon_Client *gcc;
    Instance *inst;
    Config_Item *ci;
    Ei8k *ei8k;

    inst = E_NEW (Instance, 1);
    ci = _ei8k_config_item_get (id);
    ei8k = _ei8k_new (gc->evas);
    ei8k->inst = inst;
    inst->ei8k = ei8k;

    o = ei8k->ei8k_obj;
    gcc = e_gadcon_client_new (gc, name, id, style, o);
    gcc->data = inst;
    inst->gcc = gcc;
    inst->ei8k_obj = o;

    evas_object_event_callback_add (o, EVAS_CALLBACK_MOUSE_DOWN,
                                    _ei8k_cb_mouse_down, inst);
    ei8k_config->instances = evas_list_append (ei8k_config->instances, inst);

    if (!inst->monitor)
        inst->monitor = ecore_timer_add (1, _ei8k_cb_check, inst);

    return gcc;
}



static void
_gc_shutdown (E_Gadcon_Client * gcc)
{
    Instance *inst;
    Ei8k *ei8k;

    if (!gcc)
        return;
    if (!gcc->data)
        return;

    inst = gcc->data;
    ei8k = inst->ei8k;
    if (!ei8k)
        return;

    if (inst->monitor != NULL)
        ecore_timer_del (inst->monitor);

    ei8k_config->instances = evas_list_remove (ei8k_config->instances, inst);
    evas_object_event_callback_del (ei8k->ei8k_obj, EVAS_CALLBACK_MOUSE_DOWN,
                                    _ei8k_cb_mouse_down);

    _ei8k_free (ei8k);
    free (inst);
    inst = NULL;
}



static void
_gc_orient (E_Gadcon_Client * gcc)
{
    e_gadcon_client_aspect_set (gcc, 16, 16);
    e_gadcon_client_min_size_set (gcc, 16, 16);
}



static char *
_gc_label (void)
{
    return D_ ("Ei8k");
}



static Evas_Object *
_gc_icon (Evas * evas)
{
    Evas_Object *o;
    char buf[4096];

    o = edje_object_add (evas);
    snprintf (buf, sizeof (buf), "%s/module.edj",
              e_module_dir_get (ei8k_config->module));
    edje_object_file_set (o, buf, "icon");

    return o;
}



static void
_ei8k_cb_mouse_down (void *data, Evas * e, Evas_Object * obj, void *event_info)
{
    Instance *inst;
    Evas_Event_Mouse_Down *ev;

    if (ei8k_config->menu != NULL)
        return;

    inst = data;
    ev = event_info;

    if (ev->button == 3)
    {
        E_Menu *mn;
        E_Menu_Item *mi;
        int x, y, w, h;

        mn = e_menu_new ();
        e_menu_post_deactivate_callback_set (mn, _ei8k_menu_cb_post, inst);
        ei8k_config->menu = mn;

        mi = e_menu_item_new (mn);
        e_menu_item_label_set (mi, D_ ("Configuration"));
        e_util_menu_item_edje_icon_set (mi, "enlightenment/configuration");
        e_menu_item_callback_set (mi, _ei8k_menu_cb_configure, inst);

        mi = e_menu_item_new (mn);
        e_menu_item_separator_set (mi, 1);

        e_gadcon_client_util_menu_items_append (inst->gcc, mn, 0);
        e_gadcon_canvas_zone_geometry_get (inst->gcc->gadcon, &x, &y, &w, &h);
        e_menu_activate_mouse (
            mn, e_util_zone_current_get (
                e_manager_current_get()),
            x + ev->output.x,
            y + ev->output.y, 1, 1,
            E_MENU_POP_DIRECTION_DOWN, ev->timestamp);

        evas_event_feed_mouse_up (inst->gcc->gadcon->evas, ev->button,
                                  EVAS_BUTTON_NONE, ev->timestamp, NULL);
    }
}



static void
_ei8k_menu_cb_post (void *data, E_Menu * m)
{
    if (ei8k_config->menu == NULL)
        return;
    e_object_del (E_OBJECT (ei8k_config->menu));
    ei8k_config->menu = NULL;
}



static void
_ei8k_menu_cb_configure (void *data, E_Menu * m, E_Menu_Item * mi)
{
    Instance *inst;
    Config_Item *ci;

    inst = data;
    ci = _ei8k_config_item_get (inst->gcc->id);
    _config_ei8k_module (ci);
}



static Config_Item *
_ei8k_config_item_get (const char *id)
{
    return ei8k_config->items->data;
}



void
_ei8k_config_updated (const char *id)
{
    Evas_List *l;
    Config_Item *ci;

    ci = _ei8k_config_item_get (id);

    if (ei8k_config == NULL)
        return;
}


_ei8k_error_message_dialog(char *text)
{
    E_Dialog *dia;

    dia = e_dialog_new(e_container_current_get(e_manager_current_get()), "E", "_e_mod_ei8k_error");
    if (!dia)
        return 0;
    e_dialog_title_set(dia, "Enlightenment Ei8k Module");
    e_dialog_icon_set(dia, "enlightenment/e", 64);
    e_dialog_text_set(dia, text);
    e_dialog_button_add(dia, _("OK"), NULL, NULL, NULL);
    e_win_centered_set(dia->win, 1);
    e_dialog_show(dia);
}



EAPI E_Module_Api e_modapi = { E_MODULE_API_VERSION, "Ei8k" };

EAPI void *
e_modapi_init (E_Module * m)
{

    i8k_fd = open(I8K_PROC, O_RDONLY);
    if (i8k_fd < 0)
    {
        _ei8k_error_message_dialog("Please check that the i8k driver is loaded correctly. <br>"
                                   "HINT: on some laptops you need to use 'modprobe i8k force=1'.<br>"
                                   "Ei8k shuts down now.<br>");
        return NULL;
    }
    close(i8k_fd);

    bindtextdomain (PACKAGE, LOCALEDIR);
    bind_textdomain_codeset (PACKAGE, "UTF-8");

    conf_item_edd = E_CONFIG_DD_NEW ("Ei8k_Config_Item", Config_Item);

    E_CONFIG_VAL (conf_item_edd, Config_Item, id, STR);
    E_CONFIG_VAL (conf_item_edd, Config_Item, bat_hysteresis,   INT);
    E_CONFIG_VAL (conf_item_edd, Config_Item, bat_rf_low_temp,  INT);
    E_CONFIG_VAL (conf_item_edd, Config_Item, bat_rf_high_temp, INT);
    E_CONFIG_VAL (conf_item_edd, Config_Item, bat_lf_low_temp,  INT);
    E_CONFIG_VAL (conf_item_edd, Config_Item, bat_lf_high_temp, INT);
    E_CONFIG_VAL (conf_item_edd, Config_Item, ac_hysteresis,    INT);
    E_CONFIG_VAL (conf_item_edd, Config_Item, ac_rf_low_temp,   INT);
    E_CONFIG_VAL (conf_item_edd, Config_Item, ac_rf_high_temp,  INT);
    E_CONFIG_VAL (conf_item_edd, Config_Item, ac_lf_low_temp,   INT);
    E_CONFIG_VAL (conf_item_edd, Config_Item, ac_lf_high_temp,  INT);

    conf_edd = E_CONFIG_DD_NEW ("Ei8k_Config", Config);

    E_CONFIG_LIST (conf_edd, Config, items, conf_item_edd);

    ei8k_config = e_config_domain_load ("module.ei8k", conf_edd);

    Config_Item *ci;
    if (ei8k_config == NULL)
    {
        ei8k_config = E_NEW (Config, 1);
        ci = E_NEW (Config_Item, 1);
        ci->id = evas_stringshare_add ("0");
        ci->ac_hysteresis = 4;
        ci->ac_lf_high_temp = 55;
        ci->ac_lf_low_temp = 40;
        ci->ac_rf_high_temp = 55;
        ci->ac_rf_low_temp = 40;

        ci->bat_hysteresis = 4;
        ci->bat_lf_high_temp = 55;
        ci->bat_lf_low_temp = 40;
        ci->bat_rf_high_temp = 55;
        ci->bat_rf_low_temp = 40;
        ei8k_config->items = evas_list_append (ei8k_config->items, ci);
    }
    else
        ci = ei8k_config->items->data;

    ei8k_config->module = m;
    e_gadcon_provider_register (&_gc_class);

    _ei8k_monitor = ecore_timer_add (1, _ei8k_fan_check, ci);

    return m;
}



EAPI int
e_modapi_shutdown (E_Module * m)
{
    ei8k_config->module = NULL;
    e_gadcon_provider_unregister (&_gc_class);

    if (ei8k_config->config_dialog != NULL)
        e_object_del (E_OBJECT (ei8k_config->config_dialog));

    if (ei8k_config->menu)
    {
        e_menu_post_deactivate_callback_set (ei8k_config->menu, NULL, NULL);
        e_object_del (E_OBJECT (ei8k_config->menu));
        ei8k_config->menu = NULL;
    }

    if (_ei8k_monitor != NULL)
    {
        ecore_timer_del (_ei8k_monitor);
        _ei8k_monitor = NULL;
    }
    free (ei8k_config);

    ei8k_config = NULL;
    E_CONFIG_DD_FREE (conf_item_edd);
    E_CONFIG_DD_FREE (conf_edd);

    return 1;
}



EAPI int
e_modapi_save (E_Module * m)
{
    Evas_List *l;
    Instance *inst;
    if(ei8k_config->instances)
        inst = ei8k_config->instances->data;

    for (l = ei8k_config->instances; l; l = l->next)
    {
        Instance *inst;
        Config_Item *ci;

        inst = l->data;
        ci = _ei8k_config_item_get (inst->gcc->id);
        if (ci->id != NULL)
            evas_stringshare_del (ci->id);
        ci->id = evas_stringshare_add (inst->gcc->id);
    }
    e_config_domain_save ("module.ei8k", conf_edd, ei8k_config);

    return 1;
}



EAPI int
e_modapi_about (E_Module * m)
{
    e_module_dialog_show (m, D_ ("Enlightenment Ei8k Monitor Module"),
                          D_ ("This module is used to monitor ei8k."));
    return 1;
}



static Ei8k *
_ei8k_new (Evas * evas)
{
    Ei8k *ei8k;
    char buf[4096];

    ei8k = E_NEW (Ei8k, 1);
    snprintf (buf, sizeof (buf), "%s/ei8k.edj",
              e_module_dir_get (ei8k_config->module));

    ei8k->ei8k_obj = edje_object_add (evas);
    if (!e_theme_edje_object_set
            (ei8k->ei8k_obj, "base/theme/modules/ei8k", "modules/ei8k/main"))
        edje_object_file_set (ei8k->ei8k_obj, buf, "modules/ei8k/main");

    evas_object_show (ei8k->ei8k_obj);
    return ei8k;
}



static void
_ei8k_free (Ei8k * ei8k)
{
    evas_object_del (ei8k->ei8k_obj);
    free (ei8k);
    ei8k = NULL;
}



static int
_ei8k_cb_check (void *data)
{
    Instance *inst;
    Config_Item *ci;

    time_t ei8k;

    inst = data;
    if (!inst)
        return 0;

    char t_fan_right[10];
    char t_fan_left[10];
    char t_cputemp[10];

    sprintf(&t_fan_right[0], "right: %d", rf_stat);
    sprintf(&t_fan_left[0],  "left:   %d", lf_stat);
    sprintf(&t_cputemp[0],   "temp:  %d", temp);
    edje_object_part_text_set (inst->ei8k->ei8k_obj, "fan_right", &t_fan_right[0]);
    edje_object_part_text_set (inst->ei8k->ei8k_obj, "fan_left", &t_fan_left[0]);
    edje_object_part_text_set (inst->ei8k->ei8k_obj, "temp", &t_cputemp[0]);
    return 1;
}



static int _ei8k_fan_check(void *data)
{
    Config_Item *c = data;
    i8k_fd = open(I8K_PROC, O_RDONLY);

    if (i8k_fd < 0)
        perror("can't open " I8K_PROC);

    int ret;
    int args[1];
    ioctl(i8k_fd, I8K_GET_TEMP, &args);
    temp = args[0];

    args[0] = I8K_FAN_LEFT;
    ioctl(i8k_fd, I8K_GET_FAN, &args);
    int lf_actl_stat = args[0];

    args[0] = I8K_FAN_RIGHT;
    ioctl(i8k_fd, I8K_GET_FAN, &args);
    int rf_actl_stat = args[0];

    //printf("actl fan speed: l:%d  r:%d t: %d\n", lf_actl_stat, rf_actl_stat, temp);

    lf_stat = 0;
    rf_stat = 0;

    args[0] = 0;
    ioctl(i8k_fd, I8K_POWER_STATUS, &args);
    int power_stat = args[0]; // 1 == ac


    if(power_stat) /* TODO: use acpi/apm/dbus instead */
    {
        if (temp <= c->ac_lf_low_temp - c->ac_hysteresis)
            lf_stat = 0;
        else if (temp >= c->ac_lf_high_temp + c->ac_hysteresis)
            lf_stat = 2;
        else if ((temp >= c->ac_lf_low_temp + c->ac_hysteresis && lf_actl_stat == 0)
                 || (temp <= c->ac_lf_high_temp - c->ac_hysteresis && lf_actl_stat == 2))
            lf_stat = 1;
        else
            lf_stat = lf_actl_stat;

        if (temp <= c->ac_rf_low_temp - c->ac_hysteresis)
            rf_stat = 0;
        else if (temp >= c->ac_rf_high_temp + c->ac_hysteresis)
            rf_stat = 2;
        else if ((temp >= c->ac_rf_low_temp + c->ac_hysteresis && rf_actl_stat == 0)
                 || (temp <= c->ac_rf_high_temp - c->ac_hysteresis && rf_actl_stat == 2))
            rf_stat = 1;
        else
            rf_stat = rf_actl_stat;
    }
    else
    {
        if (temp <= c->bat_lf_low_temp - c->bat_hysteresis)
            lf_stat = 0;
        else if (temp >= c->bat_lf_high_temp + c->bat_hysteresis)
            lf_stat = 2;
        else if ((temp >= c->bat_lf_low_temp + c->bat_hysteresis && lf_actl_stat == 0)
                 || (temp <= c->bat_lf_high_temp - c->bat_hysteresis && lf_actl_stat == 2))
            lf_stat = 1;
        else
            lf_stat = lf_actl_stat;

        if (temp <= c->bat_rf_low_temp - c->bat_hysteresis)
            rf_stat = 0;
        else if (temp >= c->bat_rf_high_temp + c->bat_hysteresis)
            rf_stat = 2;
        else if ((temp >= c->bat_rf_low_temp + c->bat_hysteresis && rf_actl_stat == 0)
                 || (temp <= c->bat_rf_high_temp - c->bat_hysteresis && rf_actl_stat == 2))
            rf_stat = 1;
        else
            rf_stat = rf_actl_stat;
    }

    int fan_args[2];
    if (lf_actl_stat != lf_stat)
    {
        // printf("change fan left: %d\n", lf_stat);

        fan_args[0] = I8K_FAN_LEFT;
        fan_args[1] = lf_stat;
        if ((ioctl(i8k_fd, I8K_SET_FAN, &fan_args)) < 0)
            printf("ERROR ei8k set lf");
    }
    if(rf_actl_stat != rf_stat)
    {
        // printf("change fan right: %d\n", rf_stat);
        fan_args[0] = I8K_FAN_RIGHT;
        fan_args[1] = rf_stat;
        if ((ioctl(i8k_fd, I8K_SET_FAN, &fan_args)) < 0)
            printf("ERROR ei8k set rf");
    }

    close(i8k_fd);

    return 1;
}


